﻿using JGSY.SerialCommunication.UpperComputer.Component;
using JGSY.SerialCommunication.UpperComputer.Core;
using JGSY.SerialCommunication.UpperComputer.Core.EnumType;
using RJCP.IO.Ports;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JGSY.SerialCommunication.UpperComputer.Modbus
{
    public class ModbusAscii : ModbusBase
    {
        private bool _disposed = false;

        /// <summary>
        /// 构造函数注入通信单元
        /// </summary>
        /// <param name="portName">串口号</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="dataBit">数据位</param>
        /// <param name="parity">校验位</param>
        /// <param name="stopBits">停止位</param>
        /// <param name="readTimeout">读取超时时间(毫秒)</param>
        public ModbusAscii(string portName, int baudRate, int dataBit, Parity parity, StopBits stopBits, int readTimeout = 50)
        {
            if (string.IsNullOrWhiteSpace(portName))
                throw new ArgumentException("串口号不能为空", nameof(portName));
            if (baudRate <= 0)
                throw new ArgumentOutOfRangeException(nameof(baudRate), "波特率必须大于0");
            if (dataBit < 5 || dataBit > 8)
                throw new ArgumentOutOfRangeException(nameof(dataBit), "数据位必须在5-8之间");
            if (readTimeout <= 0)
                throw new ArgumentOutOfRangeException(nameof(readTimeout), "超时时间必须大于0");

            this.CommunicationUnit = new Serial(portName, baudRate, dataBit, parity, stopBits, readTimeout);
        }

        public override byte[] SendRequest(List<byte> pdu)
        {
            if (pdu == null || pdu.Count == 0)
                throw new ArgumentNullException(nameof(pdu), "PDU不能为空");

            // 确保串口资源被正确释放
            try
            {
                // 打开串口
                var connectState = CommunicationUnit.Open(100);
                if (!connectState.Status)
                {
                    throw new InvalidOperationException($"无法打开串口: {connectState.Message}");
                }

                // 构建ASCII消息帧
                string asciiFrame = BuildAsciiFrame(pdu);
                byte[] request = Encoding.ASCII.GetBytes(asciiFrame);

                var repContent = ModbusLengthCalculator.ExtractDataFromRequest(ModbusProtocolType.Ascii, request.ToArray());
                int functionCode = repContent.FunctionCode;
                int dataCount = repContent.DataCount;
                int receiveLen = ModbusLengthCalculator.CalculateNormalResponseLength(ModbusProtocolType.Ascii, functionCode, dataCount);

                int errrorLen = ModbusLengthCalculator.CalculateExceptionResponseLength(ModbusProtocolType.Ascii);

                // 发送请求并接收响应
                var result = ((Serial)CommunicationUnit).SendAndRecevice(request.ToList(), receiveLen, errrorLen);

                if (!result.Status || result.Datas == null || result.Datas.Count == 0)
                {
                    throw new InvalidOperationException(
                        $"通信失败: {result.Message ?? "未收到响应数据"}");
                }

                // 转换为ASCII字符串并解析
                string responseAscii = Encoding.ASCII.GetString(result.Datas.ToArray());
                return ParseAsciiFrame(responseAscii);
            }
            finally
            {
                // 确保串口被关闭
                CommunicationUnit?.Close();

            }
        }

        // 构建Modbus ASCII帧
        private string BuildAsciiFrame(List<byte> pdu)
        {
            // 帧格式: : + 从站地址(2字符) + 功能码(2字符) + 数据(2N字符) + LRC(2字符) + CR + LF
            var frame = new StringBuilder();
            frame.Append(':'); // 起始符

            // 添加PDU字节的ASCII表示
            foreach (byte b in pdu)
            {
                frame.AppendFormat("{0:X2}", b);
            }

            // 计算并添加LRC校验
            byte lrc = CalculateLrc(pdu.ToArray());
            frame.AppendFormat("{0:X2}", lrc);

            frame.Append("\r\n"); // 结束符

            return frame.ToString();
        }

        // 解析Modbus ASCII帧
        private byte[] ParseAsciiFrame(string frame)
        {
            if (string.IsNullOrEmpty(frame))
                throw new ArgumentNullException(nameof(frame), "响应帧不能为空");

            // 移除起始符和结束符
            if (!frame.StartsWith(":") || !frame.EndsWith("\r\n"))
                throw new FormatException("无效的ASCII帧格式");

            // 提取数据部分（去掉起始符和结束符）
            string dataPart = frame.Substring(1, frame.Length - 1 - 2); // 去掉':'和"\r\n"

            // 验证长度是否为偶数
            if (dataPart.Length % 2 != 0)
                throw new FormatException("ASCII帧数据部分长度必须为偶数");

            // 转换为字节数组
            int byteCount = dataPart.Length / 2;
            byte[] bytes = new byte[byteCount];

            for (int i = 0; i < byteCount; i++)
            {
                string byteStr = dataPart.Substring(i * 2, 2);
                if (!byte.TryParse(byteStr, System.Globalization.NumberStyles.HexNumber, null, out bytes[i]))
                {
                    throw new FormatException($"无效的十六进制数据: {byteStr}");
                }
            }

            // 验证LRC（至少需要有从站地址、功能码和LRC）
            if (bytes.Length < 3)
                throw new InvalidDataException("响应帧长度不足");

            // 计算LRC（不包括最后的LRC字节）
            byte[] dataBytes = new byte[bytes.Length - 1];
            Array.Copy(bytes, 0, dataBytes, 0, dataBytes.Length);

            byte receivedLrc = bytes[bytes.Length - 1];
            byte calculatedLrc = CalculateLrc(dataBytes);

            if (receivedLrc != calculatedLrc)
                throw new InvalidDataException($"LRC校验失败。接收: {receivedLrc:X2}, 计算: {calculatedLrc:X2}");

            return dataBytes;
        }

        // 计算Modbus ASCII LRC校验
        private byte CalculateLrc(byte[] data)
        {
            if (data == null || data.Length == 0)
                return 0;

            byte lrc = 0;
            foreach (byte b in data)
            {
                lrc += b;
            }

            // 取反加1（二进制补码）
            lrc = (byte)((lrc ^ 0xFF) + 1);
            return lrc;
        }

        // 实现IDisposable接口
        new public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected override void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    // 释放托管资源
                    CommunicationUnit?.Close();

                }

                _disposed = true;
            }
        }

        ~ModbusAscii()
        {
            Dispose(false);
        }
    }
}